/*
 * Copyright 2022-2027 中国信息通信研究院云计算与大数据研究所
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */ 
package com.service.impl;

import java.math.BigDecimal;
import java.sql.Time;
import java.util.Date;
import java.util.List;
import java.util.concurrent.Callable;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;

import com.common.constant.AccountConstant;
import com.common.constant.PropertiesConstant;
import com.common.constant.TrancfgConstant;
import com.common.context.SpringContextUtils;
import com.mapper.Salary;
import com.mapper.Transfer;
import com.pojo.Account;
import com.pojo.Branch;
import com.pojo.Customer;
import com.pojo.SalaryBranchCount;
import com.pojo.Salarylist;
import com.pojo.Sjno;
import com.pojo.Trancfg;
import com.pojo.Tranlist;
import com.pojo.Tranlog;
import com.service.SalaryService;

@Service
@Scope("prototype")
public class SalaryServiceImpl implements SalaryService,Callable<Long> {

    private final Logger logger = LoggerFactory.getLogger(SalaryServiceImpl.class); 

    @Autowired
    DataSourceTransactionManager dataSourceTransactionManager;
    @Autowired
    TransactionDefinition transactionDefinition;
    @Autowired
    private Salary salary;
    List<SalaryBranchCount> branchCountList;
    @Autowired
    private PropertiesConstant propertiesConstant;
    @Autowired
    private Transfer transfer;
    private String process_id;
    private Trancfg tf;

    /**
     * 代发工资每线程重复执行次数
     */
    private int salaryRepeatNum;
    
    
	public Long call() {
    	long countTime = System.currentTimeMillis();
		if(branchCountList!=null&&branchCountList.size()>0) {
			for (int i = 0; i < salaryRepeatNum; i++) {
				for(SalaryBranchCount salaryBranchCount:branchCountList) {
					String salarylist_branchid = salaryBranchCount.getSalarylist_branchid();
					int salarylistAccountNum = salaryBranchCount.getNum();
					beginGrantSalary(salarylist_branchid,salarylistAccountNum);//开始进入发放工资流程
				}
			}
		}
        return System.currentTimeMillis()-countTime;
	}

	/**
	 * 开始发放工资
	 * @param salarylist_branchid
	 * @param salarylistAccountNum
	 */
	public void beginGrantSalary(String salarylist_branchid, int salarylistAccountNum) {
    	Date nowDate = new Date();
    	long nowMillis = nowDate.getTime();
		Time nowTime = new Time(nowMillis);
		Account account = new Account();
		account.setAccount_branchid(salarylist_branchid);
		Tranlog tranLog = null;
		int fromSeq = updateAccountBranchSeq(account);
      	if(fromSeq==AccountConstant.ACCOUNT_SEQ_FAILURE) {//更新借方网点流水号失败
      		tranLog = new Tranlog(nowDate,salarylist_branchid,fromSeq,TrancfgConstant.TRANCFG_ACCOUNTS_SALARY
        			,nowDate.getTime(),System.currentTimeMillis(),(int)(System.currentTimeMillis()-nowMillis),AccountConstant.ACCOUNT_TRAN_FAILURE,"",process_id);
			logger.error("SalaryServiceImpl beginGrantSalary updateAccountBranchSeq branch {} seq {} rollback ", salarylist_branchid,fromSeq);
			return;
      	}else {
			logger.info("SalaryServiceImpl beginGrantSalary updateAccountBranchSeq branch {} seq {} success ",salarylist_branchid,fromSeq);
      	}
      	try {
      		String accountFrom = account.getAccount_id();//工资借方账号
      		Account from = null;
      		try {
        		from = transfer.readAccount(accountFrom);
			} catch (Exception e) {
				logger.error("SalaryServiceImpl grantSalary readAccount accountFrom {}" 
						,accountFrom);
				tranLog = new Tranlog(nowDate,account.getAccount_branchid(),fromSeq,TrancfgConstant.TRANCFG_ACCOUNTS_SALARY
            			,nowDate.getTime(),System.currentTimeMillis(),(int)(System.currentTimeMillis()-nowMillis),AccountConstant.ACCOUNT_TRAN_FAILURE,"",process_id);
				insertAccountTranlog(tranLog,0);
				return;
			}
    		insertBreakPoint();
    		if(from==null) {
    			logger.info("SalaryServiceImpl beginGrantSalary Account from accountFromId {} is null ", accountFrom);
    			return;
    		}
            if((from.getAccount_stat().equals(AccountConstant.ACCOUNT_NORMAL)
    				||from.getAccount_stat().equals(AccountConstant.ACCOUNT_ONLYOUT))) {
            	tranLog = grantSalary(salarylist_branchid,accountFrom,tranLog,fromSeq,nowTime,nowDate,nowMillis);//开始发放工资
            	if(tranLog==null) {//代发工资事务回滚
    				tranLog = new Tranlog(nowDate,from.getAccount_branchid(),fromSeq,TrancfgConstant.TRANCFG_ACCOUNTS_SALARY
                			,nowDate.getTime(),System.currentTimeMillis(),(int)(System.currentTimeMillis()-nowMillis),AccountConstant.ACCOUNT_TRAN_FAILURE,"",process_id);
    	    		logger.error("SalaryServiceImpl grantSalary rollback accountFrom {}" 
    						,accountFrom);
    			}
            }else {
    			logger.info("SalaryServiceImpl beginGrantSalary Account from {} not normal state is {} " ,accountFrom, from.getAccount_stat() );
    			tranLog = new Tranlog(nowDate,salarylist_branchid,fromSeq,TrancfgConstant.TRANCFG_ACCOUNTS_SALARY
            			,nowDate.getTime(),System.currentTimeMillis(),(int)(System.currentTimeMillis()-nowMillis),AccountConstant.ACCOUNT_TRAN_FAILURE,"",process_id);
            }
		} catch (Exception e) {
			logger.error("SalaryServiceImpl updateAccountBranchSeq run error brachid {} e {}" 
					,account.getAccount_branchid(),e);
		}
		insertAccountTranlog(tranLog,0);
	}

	/**
	 * 开始发放工资
	 * @param salarylistAccountNum 
	 * @param salarylist_branchid
	 * @param accountFrom
	 * @param tranLog 
	 * @param nowTime 
	 * @param fromSeq 
	 * @param nowDate 
	 * @param nowMillis 
	 * @return
	 */
	private Tranlog grantSalary(String salarylist_branchid, String accountFrom, Tranlog tranLog, int fromSeq, Time nowTime, Date nowDate, long nowMillis) {
		TransactionStatus transactionStatus = null;
    	try {
    		transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
    		Account from = transfer.readTransactionalAccount(accountFrom);
    		BigDecimal money = salary.sumSalarylist(salarylist_branchid);
    		if(money==null) {
    			money = BigDecimal.valueOf(0.0);
    		}
    		
    		if(from.getAccount_bale().subtract(money).doubleValue()<=0.0) {
    			logger.info("SalaryServiceImpl grantSalary Account from  {} money {} is not enough {} ",accountFrom, from.getAccount_bale(),money );
    			tranLog = new Tranlog(nowDate,from.getAccount_branchid(),fromSeq,TrancfgConstant.TRANCFG_ACCOUNTS_SALARY
            			,nowDate.getTime(),System.currentTimeMillis(),(int)(System.currentTimeMillis()-nowMillis),AccountConstant.ACCOUNT_TRAN_FAILURE,"",process_id);
    		}else {
    			List<Salarylist> salarylist = salary.getSalarylistByBranchId(salarylist_branchid);
    			if(salarylist!=null) {
    				BigDecimal total = new BigDecimal(0);
    				for(Salarylist salary:salarylist) {
    		    		Account to = transfer.readTransactionalAccount(salary.getSalarylist_accountid());
    		    		if(to!=null&&(to.getAccount_stat().equals(AccountConstant.ACCOUNT_NORMAL)
    	    					||to.getAccount_stat().equals(AccountConstant.ACCOUNT_ONLYIN))) {
    		    			//贷方账户正常
    		    			total = total.add(salary.getSalarylist_bale());
    		    			Account at = new Account();
    		                at.setAccount_id(salary.getSalarylist_accountid());
    		                at.setAccount_bale(salary.getSalarylist_bale());
    		                transfer.transferTo(at);
    		         		logger.info("SalaryServiceImpl grantSalary update accoun to {} monney {} success ", salary.getSalarylist_accountid(),salary.getSalarylist_bale());
    		                Sjno tf = new Sjno();
    		                tf.setSjno_id(to.getAccount_sjnoid());
    		                tf.setSjno_branchid(to.getAccount_branchid());
    		                tf.setSjno_bale(salary.getSalarylist_bale());
    		                transfer.transferToSjno(tf);
    		         		logger.info("SalaryServiceImpl grantSalary update accountSjno to {} Sjon {} monney {} success ", salary.getSalarylist_accountid(),to.getAccount_sjnoid(),salary.getSalarylist_bale());
    		           		Customer customerTo = new Customer();
    		           		customerTo.setCustomer_id(to.getAccount_custid());
    		           		customerTo.setCustomer_bale(salary.getSalarylist_bale());
    		        		transfer.updateCustomerTo(customerTo);
    		        		logger.info("SalaryServiceImpl grantSalary update customerTo {} monney {} success ", to.getAccount_custid(),salary.getSalarylist_bale());
    		         		//插入tranlist记录
    		         		Tranlist toTranList = new Tranlist(nowDate,to.getAccount_branchid(),
    		         				fromSeq,nowTime,to.getAccount_id(),salary.getSalarylist_bale(),
    		                 		AccountConstant.TRANLIST_C,from.getAccount_id(),TrancfgConstant.TRANCFG_ACCOUNTS_SALARY,process_id,new BigDecimal(0));//写贷方tranlist
    		                transfer.insertTranlist(toTranList);    	
    		    		}
    				}
    				if(total.compareTo(BigDecimal.ZERO)==0) {//没有代发的账户
                		logger.info("SalaryServiceImpl grantSalary account {} has no salaryaccount ", accountFrom);
                    	tranLog = new Tranlog(nowDate,from.getAccount_branchid(),fromSeq,TrancfgConstant.TRANCFG_ACCOUNTS_SALARY
                    			,nowDate.getTime(),System.currentTimeMillis(),(int)(System.currentTimeMillis()-nowMillis),AccountConstant.ACCOUNT_TRAN_FAILURE,"",process_id);
    				}else {
    					 //更新贷方余额和贷方科目余额
                		Tranlist fromTranList = new Tranlist(nowDate,from.getAccount_branchid(), 
                        		fromSeq, nowTime,from.getAccount_id(), total,
                        		AccountConstant.TRANLIST_D, from.getAccount_id(),TrancfgConstant.TRANCFG_ACCOUNTS_SALARY,process_id,new BigDecimal(0));//写借方tranlist
                        transfer.insertTranlist(fromTranList);
                		logger.info("SalaryServiceImpl grantSalary insert from tranlist account {} success ", accountFrom);
        				//更新借方余额和借方科目余额
                		Account af = new Account();
                        af.setAccount_id(accountFrom);
                        af.setAccount_bale(total);
                        transfer.transferFrom(af);
                		logger.info("SalaryServiceImpl grantSalary update account from {} monney {} success ", accountFrom,money);
                        Sjno sf = new Sjno();
                        sf.setSjno_id(from.getAccount_sjnoid());
                        sf.setSjno_branchid(from.getAccount_branchid());
                        sf.setSjno_bale(total);
                        transfer.transferFromSjno(sf);
		         		logger.info("SalaryServiceImpl grantSalary update accountSjno from {} Sjon {} monney {} success ",accountFrom,from.getAccount_sjnoid(),total);
		           		Customer customerFrom = new Customer();
		           		customerFrom.setCustomer_id(from.getAccount_custid());
		           		customerFrom.setCustomer_bale(total);
		        		transfer.updateCustomerFrom(customerFrom);;
		        		logger.info("SalaryServiceImpl grantSalary update customerFrom {} monney {} success ", from.getAccount_custid(),total);
                    	tranLog = new Tranlog(nowDate,from.getAccount_branchid(),fromSeq,TrancfgConstant.TRANCFG_ACCOUNTS_SALARY
                    			,nowDate.getTime(),System.currentTimeMillis(),(int)(System.currentTimeMillis()-nowMillis),AccountConstant.ACCOUNT_TRAN_SUCCESS,"",process_id);
    				}
    			}
    		}
    		insertBreakPoint();
        	dataSourceTransactionManager.commit(transactionStatus);//提交
		} catch (Exception e) {
			logger.error("SalaryServiceImpl grantSalary rollback accountFrom {} e {}" 
					,accountFrom,e);
			try {
				dataSourceTransactionManager.rollback(transactionStatus);
			} catch (Exception e2) {
				logger.error("SalaryServiceImpl grantSalary rollback error accountTo {} e {}",e);
			}
	    	return null;
		}
    	return tranLog;
	}
	
	/**
     * 更新网点流水号
     * @param account
     */
    public int updateAccountBranchSeq(Account account) {
    	TransactionStatus transactionStatus = null;
    	int seq = AccountConstant.ACCOUNT_SEQ_FAILURE;
    	try {
    		transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
    		Branch branch = transfer.readTransactionalBranchSeq(account.getAccount_branchid());
        	seq = branch.getBranch_seq();//设置网点流水号
        	account.setAccount_id(branch.getBranch_dacno());//工资借方账号
    		transfer.updateBranch(branch.getBranch_id());//更新网点流水号
    		insertBreakPoint();
        	dataSourceTransactionManager.commit(transactionStatus);//提交
		} catch (Exception e) {
			e.printStackTrace();
			try {
		    	dataSourceTransactionManager.rollback(transactionStatus);
			} catch (Exception e2) {
				logger.error("SalaryServiceImpl updateAccountBranchSeq rollback error account {} branchId {} e {}",account.getAccount_id(),account.getAccount_branchid(),e);
			}
	    	logger.error("SalaryServiceImpl updateAccountBranchSeq rollback account {} branchId {} e {}" 
					,account.getAccount_id(),account.getAccount_branchid(),e);
	    	return AccountConstant.ACCOUNT_SEQ_FAILURE;

		}
		return seq;
	}
    /**
     * 插入断点
     */
    private void insertBreakPoint() {
    	if(tf!=null&&"Y".equals(tf.getTrancfg_brkpot())) {
    		try {
				Thread.sleep(Long.parseLong(tf.getTrancfg_brktime()));
			} catch (Exception e) {
		    	logger.error("TransferServiceImpl insertBreakPoint is run error e {} ",e);
			}
    	}
	}
    /**
     * 插入tranLog
     * @param tranLog
     */
    public void insertAccountTranlog(Tranlog tranLog,int repeat) {
    	TransactionStatus transactionStatus = null;
    	boolean isSuccess = false;
    	try {
    		transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
    		transfer.insertTranlog(tranLog);
    		logger.info("SalaryServiceImpl insertAccountTranlog tranlog_date {} tranlog_branchid {} tranlog_branchseq {} success "
    				,tranLog.getTranlog_date(),tranLog.getTranlog_branchid(),tranLog.getTranlog_branchseq());
    		insertBreakPoint();
        	dataSourceTransactionManager.commit(transactionStatus);//提交
        	isSuccess = true;
		} catch (Exception e) {
			logger.error("SalaryServiceImpl insertAccountTranlog rollback tranlog_date {} tranlog_branchid {} tranlog_branchseq {} e {}" 
					,tranLog.getTranlog_date(),tranLog.getTranlog_branchid(),tranLog.getTranlog_branchseq(),e);
			try {
		    	dataSourceTransactionManager.rollback(transactionStatus);
			} catch (Exception e2) {
				logger.error("SalaryServiceImpl insertAccountTranlog rollback error accountTo {} e {}",e);
			}
		}
    	if(isSuccess) {
    		LocalTranLogServiceImpl localTranLogServiceImpl = SpringContextUtils.getBean(LocalTranLogServiceImpl.class);
        	localTranLogServiceImpl.setTranlog(tranLog);
        	localTranLogServiceImpl.start();
    	}else {
			logger.error("========>重试次数"+repeat+"===>"+tranLog.getTranlog_branchid());
    		if(repeat<propertiesConstant.getRepeat()) {
    			try {
					Thread.sleep(500);
	    	 		insertAccountTranlog(tranLog,repeat+1);
				} catch (InterruptedException e) {
					logger.error("DepositMoneyServiceImpl insertAccountTranlog Thread error tranlog_date {} tranlog_branchid {} tranlog_branchseq {} e {}" 
							,tranLog.getTranlog_date(),tranLog.getTranlog_branchid(),tranLog.getTranlog_branchseq(),e);
				}
    		}
    	}
	}

	public List<SalaryBranchCount> getBranchCountList() {
		return branchCountList;
	}

	public void setBranchCountList(List<SalaryBranchCount> branchCountList) {
		this.branchCountList = branchCountList;
	}

	public int getSalaryRepeatNum() {
		return salaryRepeatNum;
	}

	public void setSalaryRepeatNum(int salaryRepeatNum) {
		this.salaryRepeatNum = salaryRepeatNum;
	}

	public String getProcess_id() {
		return process_id;
	}

	public void setProcess_id(String process_id) {
		this.process_id = process_id;
	}

	public Trancfg getTf() {
		return tf;
	}

	public void setTf(Trancfg tf) {
		this.tf = tf;
	}

	
    
}